//
//  RegistrationViewController.swift
//  PorscheRegistration
//
//  Created by Jinzhu Liu on 2023/11/15.
//

import UIKit
import Combine

enum Section {
    case main
}

typealias CellViewModel = AnyObject

struct RowItem: Hashable {
    var id: Int
    var viewModel: CellViewModel
    
    
    init(id: Int, viewModel: CellViewModel) {
        self.id = id
        self.viewModel = viewModel
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    static func == (lhs: RowItem, rhs: RowItem) -> Bool {
        lhs.id == rhs.id
    }
}

class RegistrationViewController: UIViewController, UINavigationControllerDelegate {
    
    struct Constant {
        static let unknowCellIdentifier = "UnknowType"
    }
    
    typealias DataSource = UITableViewDiffableDataSource<Section, RowItem>
    typealias Snapshot = NSDiffableDataSourceSnapshot<Section, RowItem>

    private var cancelable = Set<AnyCancellable>()
    private lazy var dataSource = makeDataSource()
    var viewModel = RegistrationViewModel()
    var colorPickerViewModel = ColorPickerViewModel()
    var rowItemList: [RowItem] = []
    var tableView: UITableView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupUI()
        setupLayouts()
        
        viewModel.$cellViewModels.sink { [unowned self] cellViewModels in
            self.generateRowItems(with: cellViewModels)
            self.applySnapshot()
        }.store(in: &cancelable)
        
        viewModel.showPhotoPicker.sink { [unowned self] _ in
            self.pickPhoto()
        }.store(in: &cancelable)
        
        colorPickerViewModel.output.selectedColorPublisher
            .sink { [unowned self] avatarColor in
                viewModel.input.selectedAvatarColor(avatarColor)
            }.store(in: &cancelable)
        
        viewModel.start()
        
        applySnapshot(animatingDifferences: false)
    }
    
    func generateRowItems(with cellViewModels: [RegistrationCellViewModelType]) {
        var rowItems = [RowItem]()
        for (index, cellViewModel) in cellViewModels.enumerated() {
            rowItems.append(RowItem(id: index, viewModel: cellViewModel as AnyObject))
        }
        self.rowItemList = rowItems
    }
    func applySnapshot(animatingDifferences: Bool = true) {
        guard let dataSource else { return }
        
        var snapshot = Snapshot()
        snapshot.appendSections([.main])
        snapshot.appendItems(rowItemList)
        dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
    }

    private func setupUI() {
        self.view.backgroundColor = .systemGroupedBackground
        let tableView = UITableView(frame: .zero, style: .grouped)
        tableView.backgroundColor = .clear
        tableView.estimatedRowHeight = 52.0
        tableView.delegate = self
        
        self.view.addSubview(tableView)
        
        tableView.register(RegistrationTextCell.self, forCellReuseIdentifier: RegistrationTextCell.reuseIdentifier)
        tableView.register(RegistrationAvatarCell.self, forCellReuseIdentifier: RegistrationAvatarCell.reuseIdentifier)
        tableView.register(RegistrationButtonCell.self, forCellReuseIdentifier: RegistrationButtonCell.reuseIdentifier)
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: Constant.unknowCellIdentifier)
        
        self.tableView = tableView
    }
    
    private func setupLayouts() {
        guard let tableView else { return }
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
        tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true

    }
    
    func makeDataSource() -> DataSource? {
        
        guard let tableView else { return nil }
 
        let dataSource = DataSource(tableView: tableView) { tableView, indexPath, item in
            var cell: UITableViewCell?
            let cellViewModel = item.viewModel
            switch cellViewModel {
            case is any RegistrationAvatarCellViewModelType:
                cell = tableView.dequeueReusableCell(withIdentifier: RegistrationAvatarCell.reuseIdentifier, for: indexPath)
            case is any RegistrationNameCellViewModelType:
                cell = tableView.dequeueReusableCell(withIdentifier: RegistrationTextCell.reuseIdentifier, for: indexPath)
            case is any RegistrationPhoneNumCellViewModelType:
                cell = tableView.dequeueReusableCell(withIdentifier: RegistrationTextCell.reuseIdentifier, for: indexPath)
            case is any RegistrationEmailCellViewModelType:
                cell = tableView.dequeueReusableCell(withIdentifier: RegistrationTextCell.reuseIdentifier, for: indexPath)
            case is any RegistrationColorCellViewModelType:
                cell = tableView.dequeueReusableCell(withIdentifier: RegistrationTextCell.reuseIdentifier, for: indexPath)
            case is any RegistrationButtonCellViewModelType:
                cell = tableView.dequeueReusableCell(withIdentifier: RegistrationButtonCell.reuseIdentifier, for: indexPath)
            default:
                cell = tableView.dequeueReusableCell(withIdentifier: Constant.unknowCellIdentifier, for: indexPath)
            }
            cell?.selectionStyle = .none
            if cell is ViewModelUpdatableCell {
                (cell as! any ViewModelUpdatableCell as ViewModelUpdatableCell).update(with: cellViewModel)
            }
            
            return cell
        }
        
        return dataSource
    }
}

extension RegistrationViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: false)
        
        let cellViewModel = rowItemList[indexPath.row].viewModel
        if cellViewModel is RegistrationColorCellViewModelType {
            showColorSelectionView()
        }
    }
    
    private func showColorSelectionView() {
        let colorPickerViewController = ColorPickerViewController(viewModel: colorPickerViewModel)
        
        self.present(colorPickerViewController, animated: false)
    }
}

extension RegistrationViewController: UIImagePickerControllerDelegate {
    public func pickPhoto() {
        let imagePicker = UIImagePickerController()
        imagePicker.sourceType = .photoLibrary
        imagePicker.delegate = self
        present(imagePicker, animated: true)
    }

    public func imagePickerController(_ picker: UIImagePickerController,
                                      didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        if let image = (info[.originalImage] as? UIImage) {
            self.viewModel.input.selectedImage(image)
        }
        picker.dismiss(animated: true, completion: nil)
    }
}
